package dict

import (
	"sync"
)

// Dict is goroutine safe map
type Dict[K comparable, V any] struct {
	mutex sync.RWMutex
	m     map[K]V
}

func New[K comparable, V any]() *Dict[K, V] {
	p := new(Dict[K, V])
	p.m = make(map[K]V)
	return p
}

func (p *Dict[K, V]) Set(key K, val V) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	p.m[key] = val
}

func (p *Dict[K, V]) Get(key K) (val V, found bool) {
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	val, found = p.m[key]
	return
}

func (p *Dict[K, V]) Delete(key K) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	delete(p.m, key)
}

func (p *Dict[K, V]) Clear() {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	p.m = make(map[K]V)
}

func (p *Dict[K, V]) Len() int {
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	return len(p.m)
}

func (p *Dict[K, V]) Clone() (m map[K]V) {
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	m = make(map[K]V, len(p.m))
	for key, val := range p.m {
		m[key] = val
	}
	return
}

// Range iter element of p with f
// if f return false, then break the range
// note: do not call any p's function inside f, otherwise will occur deadlock
func (p *Dict[K, V]) Range(f func(K, V) bool) {
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	for key, val := range p.m {
		if f(key, val) == false {
			break
		}
	}
}
